package br.ufrn.btest;

import java.util.Set;

import br.ufrn.btest.parser.Operation;
import br.ufrn.btest.utils.ConventionTools;

public class TestMachineBuilder {

	
	private Operation operation;
	private Set<String> combinations;
	private Partitioner partitioner;


	
	public TestMachineBuilder(Operation operation, Set<String> combinations) {
		this.operation = operation;
		this.combinations = combinations;
		this.partitioner = new Partitioner(operation);
	}
	
	
	public String generateTestMachine() {
		StringBuffer testMachine = new StringBuffer("");
		
		addHeader(testMachine);
		addSees(testMachine);
		addIncludes(testMachine);
		addUses(testMachine);
		addSets(testMachine);
		addConstants(testMachine);
		addProperties(testMachine);
		addOperations(testMachine);
		addFooter(testMachine);
		
		return testMachine.toString();
	}
	
	

	private void addHeader(StringBuffer testMachine) {
		testMachine.append("MACHINE " + ConventionTools.getTestMachineName(this.operation) + "\n\n");
	}
	
	
	
	private void addSees(StringBuffer testMachine) {
		if (this.operation.getMachine().getSees() != null) {
			testMachine.append(this.operation.getMachine().getSees().toString() + "\n");
		}
	}
	
	
	
	private void addIncludes(StringBuffer testMachine) {
		if (this.operation.getMachine().getIncludes() != null) {
			testMachine.append(this.operation.getMachine().getIncludes().toString() + "\n");
		}
	}

	
	
	private void addUses(StringBuffer testMachine) {
		if (this.operation.getMachine().getUses() != null) {
			testMachine.append(this.operation.getMachine().getUses().toString() + "\n");
		}
	}
	
	
	
	private void addSets(StringBuffer testMachine) {
		if (this.operation.getMachine().getSets() != null) {
			testMachine.append(this.operation.getMachine().getSets().toString());
		}
	}
	
	
	
	private void addConstants(StringBuffer testMachine) {
		if (this.operation.getMachine().getContants() != null) {
			testMachine.append(this.operation.getMachine().getContants().toString());
		}
	}
	
	
	
	private void addProperties(StringBuffer testMachine) {
		if (this.operation.getMachine().getProperties() != null) {
			testMachine.append(this.operation.getMachine().getProperties().toString());
		}
	}
	
	
	private String addOperations(StringBuffer testMachine) {
		StringBuffer testOperation = new StringBuffer("");

		testOperation.append("OPERATIONS\n");
		int operationIndex = 1;
		
		for (String combination : this.combinations) {
			addOperationForCombination(testOperation, operationIndex, combination);
			if(operationIndex < this.combinations.size()) { testOperation.append(";\n"); }
			operationIndex++;
		}

		testMachine.append(testOperation.toString());
		return testOperation.toString().trim();
	}


	private void addOperationForCombination(StringBuffer testOperation, int operationIndex, String combination) {
		addOpComment(testOperation);
		addOpName(testOperation, operationIndex);
		addOperationParameters(testOperation);
		addOperationPrecondition(testOperation, combination);
		addOperationBody(testOperation);
	}
	
	
	private void addOpComment(StringBuffer testOperation) {
		testOperation.append("/* Equivalence Class test data for " + this.operation.getName() + " */\n");
	}
	
	
	private void addOpName(StringBuffer testOperation, int index) {
		testOperation.append(this.operation.getName() + "_test" + index);
	}
	
	
	private void addOperationParameters(StringBuffer testOperation) {
		Set<String> operationInputSpace = this.partitioner.getOperationInputSpace();
		testOperation.append(formatOperationParameters(operationInputSpace));
		testOperation.append(" =\n");
	}
	
	
	private void addOperationPrecondition(StringBuffer testOperation, String formula) {
		testOperation.append("PRE\n" + formula + "\n");
	}
	
	
	private void addOperationBody(StringBuffer testOperation) {
		testOperation.append("THEN\n" + "skip\n" + "END\n");
	}
	
	
	private void addFooter(StringBuffer testMachine) {
		testMachine.append("END");
	}

	
	private String formatOperationParameters(Set<String> variables) {
		StringBuffer returnVariables = new StringBuffer("(\n");
		int varCounter = 1;
		for (String variable : variables) {
			if (varCounter == variables.size()) {
				returnVariables.append(variable + "\n)");
			} else {
				returnVariables.append(variable + ",\n");
			}
			varCounter++;
		}
		return returnVariables.toString();
	}
	
}
